// DESCRIPTION: Verilator: built-in packages and classes
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2022-2025 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU Lesser
// General Public License Version 3 or the Perl Artistic License Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilated IEEE std:: header
///
/// This file is included automatically by Verilator, unless '--no-std-package'
/// is used.
///
/// This file is not part of the Verilated public-facing API.
/// It is only for internal use.
///
//*************************************************************************
//
// The following keywords from this file are hardcoded for detection in the parser:
// "mailbox", "process", "randomize", "semaphore", "std"

// verilator lint_off DECLFILENAME
// verilator lint_off TIMESCALEMOD
// verilator lint_off UNUSEDSIGNAL
package std;
  class mailbox #(
      type T
  );
    protected int m_bound;
    protected T m_queue[$];

    function new(int bound = 0);
      m_bound = bound;
    endfunction

    function int num();
      return m_queue.size();
    endfunction

    task put(T message);
`ifdef VERILATOR_TIMING
      while (m_bound != 0 && m_queue.size() >= m_bound)  //
        wait (m_queue.size() < m_bound);
      m_queue.push_back(message);
`endif
    endtask

    function int try_put(T message);
      if (m_bound == 0 || num() < m_bound) begin
        m_queue.push_back(message);
        return 1;
      end
      return 0;
    endfunction

    task get(ref T message);
`ifdef VERILATOR_TIMING
      while (m_queue.size() == 0) begin
        wait (m_queue.size() > 0);
      end
      message = m_queue.pop_front();
`endif
    endtask

    function int try_get(ref T message);
      if (num() > 0) begin
        message = m_queue.pop_front();
        return 1;
      end
      return 0;
    endfunction

    task peek(ref T message);
`ifdef VERILATOR_TIMING
      while (m_queue.size() == 0) begin
        wait (m_queue.size() > 0);
      end
      message = m_queue[0];
`endif
    endtask

    function int try_peek(ref T message);
      if (num() > 0) begin
        message = m_queue[0];
        return 1;
      end
      return 0;
    endfunction
  endclass

  class semaphore;
    protected int m_keyCount;

    function new(int keyCount = 0);
      m_keyCount = keyCount;
    endfunction

    function void put(int keyCount = 1);
      m_keyCount += keyCount;
    endfunction

    task get(int keyCount = 1);
`ifdef VERILATOR_TIMING
      while (m_keyCount < keyCount) begin
        wait (m_keyCount >= keyCount);
      end
      m_keyCount -= keyCount;
`endif
    endtask

    function int try_get(int keyCount = 1);
      if (m_keyCount >= keyCount) begin
        m_keyCount -= keyCount;
        return 1;
      end
      return 0;
    endfunction
  endclass

  class process;
    typedef enum {
      FINISHED = 0,
      RUNNING = 1,
      WAITING = 2,
      SUSPENDED = 3,
      KILLED = 4
    } state;

    // Width visitor changes it to VlProcessRef
    protected chandle m_process;

    static function process self();
      process p = new;
`ifdef VERILATOR_TIMING
      $c(p.m_process, " = vlProcess;");
`endif
      return p;
    endfunction

    protected function void set_status(state s);
`ifdef VERILATOR_TIMING
      $c(m_process, "->state(", s, ");");
`endif
    endfunction

    function state status();
`ifdef VERILATOR_TIMING
      return state'($cpure(m_process, "->state()"));
`else
      return RUNNING;
`endif
    endfunction

    function void kill();
      set_status(KILLED);
    endfunction

    function void suspend();
      $error("std::process::suspend() not supported");
    endfunction

    function void resume();
      set_status(RUNNING);
    endfunction

    task await();
`ifdef VERILATOR_TIMING
      wait (status() == FINISHED || status() == KILLED);
`endif
    endtask

    static task killQueue(ref process processQueue[$]);
`ifdef VERILATOR_TIMING
      while (processQueue.size() > 0) begin
        processQueue.pop_back().kill();
      end
`endif
    endtask

    // Two process references are equal if the different classes' containing
    // m_process are equal. Can't yet use <=> as the base class template
    // comparisons doesn't define <=> as they don't yet require --timing and C++20.
    // verilog_format: off
`ifdef VERILATOR_TIMING
`systemc_header_post
template<> template<>
inline bool VlClassRef<`systemc_class_name>::operator==(const VlClassRef<`systemc_class_name>& rhs) const {
    if (!m_objp && !rhs.m_objp) return true;
    if (!m_objp || !rhs.m_objp) return false;
    return m_objp->__PVT__m_process == rhs.m_objp->__PVT__m_process;
};
template<> template<>
inline bool VlClassRef<`systemc_class_name>::operator!=(const VlClassRef<`systemc_class_name>& rhs) const {
    if (!m_objp && !rhs.m_objp) return false;
    if (!m_objp || !rhs.m_objp) return true;
    return m_objp->__PVT__m_process != rhs.m_objp->__PVT__m_process;
};
template<> template<>
inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc_class_name>& rhs) const {
    if (!m_objp && !rhs.m_objp) return false;
    if (!m_objp || !rhs.m_objp) return false;
    return m_objp->__PVT__m_process < rhs.m_objp->__PVT__m_process;
};
`verilog
`endif
    // verilog_format: on

    // When really implemented, srandom must operate on the process, but for
    // now rely on the srandom() that is automatically generated for all
    // classes.
    //
    // function void srandom(int seed);
    // endfunction

    // The methods below access the common RNG, full support
    // of get_randstate/set_randstate requires accessing the RNG state
    // of the specified process (see IEEE 1800-2023, 18.14.), but as for
    // now processes do not have their own RNGs.
    function string get_randstate();
      // Initialize with $c to ensure it won't be constified
      string s = string'($c("0"));

      $c(s, " = ", m_process, "->randstate();");
      return s;
    endfunction

    function void set_randstate(string s);
      $c(m_process, "->randstate(", s, ");");
    endfunction
  endclass

  function int randomize();
    randomize = 0;
  endfunction
endpackage
